import 'dart:async';
import 'dart:convert';

import 'package:date_format/date_format.dart';
import 'package:get/get.dart';
import 'package:rongcloud_im_wrapper_plugin/rongcloud_im_wrapper_plugin.dart';
import 'package:zootix/models/zt_im_message.model.dart';
import 'package:zootix/models/zt_im_conversation.model.dart';
import 'package:zootix/models/zt_im_strategy.dart';
import 'package:zootix/services/zt_service.dart';
import 'package:zootix/apis/zt-api.dart' as Api;
import 'package:zootix/store/zt_auth.store.dart';
import 'package:zootix/util/zt_loading_util.dart';
import 'package:zootix/util/zt_logger_util.dart';
import 'package:zootix/util/zt_snack_bar.util.dart';

class ZtImChartStore extends GetxController {
  static ZtImChartStore get to => Get.find();
  final conversationList = <ZtImConversationModel>[].obs;
  ZtIMStrategy? imStrategy;
  RCIMIWEngine? imEngine; //融云引擎
  String? imToken;
  @override
  void onClose() {
    ZtLoggerUtil().info('[Store-Im] close imchat');
    super.onClose();
  }

  Future<ZtImChartStore> init() async {
    try {
      await getIMStrategy();
      await getIMToken();
      await initIMEngine();
      await connectIM();
      onConnectionStatusChanged();
      onMessageReceived();
      onPrivateReadReceiptReceived();
    } catch (e) {
      printError(info: '$e');
    }
    return this;
  }

  Future<bool> addToBlacklist(String userId) async {
    IRCIMIWAddToBlacklistCallback callback = IRCIMIWAddToBlacklistCallback(
        onBlacklistAdded: (int? code, String? userId) => {});
    int? ret = await imEngine?.addToBlacklist(userId, callback: callback);
    return ret == 0;
  }

  Future<bool> removeFromBlacklist(String userId) async {
    IRCIMIWRemoveFromBlacklistCallback callback =
        IRCIMIWRemoveFromBlacklistCallback(
            onBlacklistRemoved: (int? code, String? userId) => {});
    int? ret = await imEngine?.removeFromBlacklist(userId, callback: callback);
    return ret == 0;
  }

  Future<void> clearUnreadCount(String targetId, int timestamp) async {
    IRCIMIWClearUnreadCountCallback? callback =
        IRCIMIWClearUnreadCountCallback(onUnreadCountCleared: (int? code) {});
    await imEngine?.clearUnreadCount(
        RCIMIWConversationType.private, targetId, null, timestamp,
        callback: callback);
  }

  void onPrivateReadReceiptReceived() {
    imEngine?.onPrivateReadReceiptReceived =
        (String? targetId, String? channelId, int? timestamp) {
      ZtLoggerUtil().info(
          '[Store Chat] onPrivateReadReceiptReceived $targetId, $channelId, $timestamp');
    };
  }

  /// Sends a private read receipt message to the specified target ID with the given timestamp.
  Future<void> sendPrivateReadReceiptMessage(
      String targetId, int timestamp) async {
    IRCIMIWSendPrivateReadReceiptMessageCallback? callback =
        IRCIMIWSendPrivateReadReceiptMessageCallback(
            onPrivateReadReceiptMessageSent: (int? code) {});

    int? ret = await imEngine?.sendPrivateReadReceiptMessage(
        targetId, null, timestamp,
        callback: callback);
    if (ret == 0) {}
  }

  Future<List<ZtIMessageModel>> getMessages(
      {required String targetId,
      int? limit,
      int? sentTime,
      RCIMIWMessageOperationPolicy? policy}) async {
    Completer<List<ZtIMessageModel>> completer =
        Completer<List<ZtIMessageModel>>();
    IRCIMIWGetMessagesCallback? callback =
        IRCIMIWGetMessagesCallback(onSuccess: (List<RCIMIWMessage>? t) {
      ZtLoggerUtil().info(t.toString());
      List<ZtIMessageModel> result = [];
      if (t != null) {
        result = t.map<ZtIMessageModel>((e) {
          return processImMessage(e);
        }).toList();
      }
      completer.complete(result);
    }, onError: (int? code) {
      ZtLoggerUtil().error('[Store Chat] getMessages error $code');
      completer.completeError([]);
    });

    int? res = await imEngine?.getMessages(
        RCIMIWConversationType.private,
        targetId,
        null,
        sentTime ?? DateTime.now().millisecondsSinceEpoch,
        RCIMIWTimeOrder.before,
        RCIMIWMessageOperationPolicy.localRemote,
        limit ?? 20,
        callback: callback);
    if (res != 0) {
      completer.completeError('Get messages failed. Please try again.');
    }
    return completer.future;
  }

  Future<bool> sendMediaMessage(
      {required String userId,
      required String path,
      int? duration,
      RCIMIWMessageType messageType = RCIMIWMessageType.voice,
      String? avatar,
      String? nickname,
      Function? onMessageSent,
      Function? onMediaMessageSending,
      Function? onMessageSaved}) async {
    if (imEngine != null) {
      RCIMIWMediaMessage? mediaMessage;
      switch (messageType) {
        case RCIMIWMessageType.voice:
          mediaMessage = await imEngine?.createVoiceMessage(
              RCIMIWConversationType.private, userId, null, path, duration!);
          break;
        case RCIMIWMessageType.image:
          mediaMessage = await imEngine?.createImageMessage(
              RCIMIWConversationType.private, userId, null, path);
          break;
        case RCIMIWMessageType.file:
          mediaMessage = await imEngine?.createFileMessage(
              RCIMIWConversationType.private, userId, null, path);
          break;
        default:
      }

      RCIMIWSendMediaMessageListener? listener = RCIMIWSendMediaMessageListener(
          onMediaMessageSaved: (RCIMIWMediaMessage? message) {
        if (onMessageSaved != null) {
          onMessageSaved(message);
        }
      }, onMediaMessageSending: (RCIMIWMediaMessage? message, int? progress) {
        if (onMediaMessageSending != null) {
          onMediaMessageSending(message);
        }
      }, onSendingMediaMessageCanceled: (RCIMIWMediaMessage? message) {
        print('onSendingMediaMessageCanceled');
      }, onMediaMessageSent: (int? code, RCIMIWMediaMessage? message) {
        if (onMessageSent != null) {
          onMessageSent(message);
        }
      });

      if (mediaMessage != null) {
        mediaMessage.userInfo = RCIMIWUserInfo.create(
            userId: userId,
            name: nickname,
            portrait: avatar,
            extra: jsonEncode({
              'avatar': ZtAuthStore.to.userInfo.value.avatarThumbUrl,
              'userId': ZtAuthStore.to.userInfo.value.userId,
              'nickname': ZtAuthStore.to.userInfo.value.nickname
            }));
        int? res =
            await imEngine?.sendMediaMessage(mediaMessage, listener: listener);
        return res == 0;
      }
    }
    return false;
  }

  Future<bool> sendMessage(
      {required String userId,
      required String message,
      String? avatar,
      String? nickname,
      Function? onMessageSent,
      Function? onMessageSaved}) async {
    if (imEngine != null) {
      RCIMIWMessage? textMessage = await imEngine?.createTextMessage(
        RCIMIWConversationType.private,
        userId,
        null,
        message,
      );

      if (textMessage != null) {
        textMessage.userInfo = RCIMIWUserInfo.create(
            userId: userId,
            name: nickname,
            portrait: avatar,
            extra: jsonEncode({
              'avatar': ZtAuthStore.to.userInfo.value.avatarThumbUrl,
              'userId': ZtAuthStore.to.userInfo.value.userId,
              'nickname': ZtAuthStore.to.userInfo.value.nickname
            }));
        int? res = await imEngine?.sendMessage(textMessage,
            callback: RCIMIWSendMessageCallback(
                onMessageSent: (int? index, RCIMIWMessage? message) {
              if (onMessageSent != null) {
                onMessageSent(message);
              }
            }, onMessageSaved: (RCIMIWMessage? message) {
              if (onMessageSaved != null) {
                onMessageSaved(message);
              }
            }));
        return res == 0;
      }
    }
    return false;
  }

  Future<List<ZtImConversationModel>> getConversations(
      {int? limit, int? time, bool? forceRender = false}) async {
    Completer<List<ZtImConversationModel>> completer =
        Completer<List<ZtImConversationModel>>();
    ZtLoggerUtil().info(DateTime.now().millisecondsSinceEpoch);
    IRCIMIWGetConversationsCallback? callback = IRCIMIWGetConversationsCallback(
        onSuccess: (List<RCIMIWConversation>? t) {
      List<ZtImConversationModel> result = [];
      if (t != null) {
        result = t.map<ZtImConversationModel>((e) {
          ZtImConversationModel conversationModel = ZtImConversationModel(
              userId: e.targetId,
              conversationType: e.conversationType,
              nickname: e.lastMessage?.userInfo?.name ?? '',
              avatarUrl: e.lastMessage?.userInfo?.portrait ?? '',
              unreadCount: e.unreadCount,
              lastMessage: e.lastMessage?.toJson()['text'],
              lastMessageType: e.lastMessage?.messageType,
              operationTime: e.operationTime!);
          // i am the recipient, must show the extra info
          if (e.lastMessage?.userInfo?.userId ==
              ZtAuthStore.to.userInfo.value.userId) {
            if (e.lastMessage?.userInfo?.extra != null) {
              final extraInfo = jsonDecode(e.lastMessage!.userInfo!.extra!);
              conversationModel.avatarUrl = extraInfo['avatar'];
              conversationModel.nickname = extraInfo['nickname'];
              conversationModel.userId = extraInfo['userId'];
            }
          }
          return conversationModel;
        }).toList();
      }
      if (forceRender == true) {
        conversationList.assignAll(result);
      }
      completer.complete(result);
    }, onError: (int? code) {
      ZtSnackBarUtil().error('Get conversation error.$code');
      completer.completeError([]);
    });
    if (imEngine == null) {
      completer.completeError('IM engine not exsited. Initing');
      init().then((value) {
        getConversations(limit: limit, time: time, forceRender: true);
      });
      return completer.future;
    }
    int? res = await imEngine?.getConversations(
        [RCIMIWConversationType.private],
        null,
        time ?? DateTime.now().millisecondsSinceEpoch,
        limit ?? 30,
        callback: callback);
    if (res != 0) {
      completer.completeError('Get messages failed.');
    }
    return completer.future;
  }

  void onMessageReceived() {
    imEngine?.onMessageReceived = (RCIMIWMessage? message, int? left,
        bool? offline, bool? hasPackage) async {
      if (message != null) {
        final result = await getConversations(limit: 20);
        conversationList.assignAll(result);
      }
    };
  }

  Future<void> connectIM() async {
    RCIMIWConnectCallback? callback =
        RCIMIWConnectCallback(onDatabaseOpened: (int? code) {
      ZtLoggerUtil().info('[Store Chat] onDatabaseOpened,code:$code');
    }, onConnected: (int? code, String? userId) {
      ZtLoggerUtil().info('[Store Chat] onConnected,code:$code,userId:$userId');
    });
    if (imToken != null) {
      int? ret = await imEngine?.connect(imToken!, 5, callback: callback);
    }
  }

  Future<void> disconnectIM() async {
    await imEngine?.disconnect(false);
  }

  Future<void> initIMEngine() async {
    RCIMIWEngineOptions options = RCIMIWEngineOptions.create();
    final rck = ZtAppInfoService.to.getConfigByCode<String>('rck');
    imEngine = await RCIMIWEngine.create(rck, options);
    ZtLoggerUtil().info(imEngine);
  }

  void onConnectionStatusChanged() {
    imEngine?.onConnectionStatusChanged = (RCIMIWConnectionStatus? status) {
      ZtLoggerUtil().info(status);
    };
  }

  Future<bool> removeConversation(
      String targetId, RCIMIWConversationType type) async {
    int? ret = await imEngine?.removeConversation(
      type,
      targetId,
      null,
    );
    if (ret == 0) {
      conversationList.removeWhere(((item) => item.userId == targetId));
    }
    return ret == 0;
  }

  Future<void> getIMToken() async {
    final response = await Api.getIMToken();
    if (response.data != null) {
      imToken = response.data['data'];
    }
  }

  Future<void> getIMStrategy() async {
    final response = await Api.getIMStrategy();
    if (response.data != null) {
      imStrategy = ZtIMStrategy.fromJson(response.data['data']);
    }
  }

  ZtIMessageModel processImMessage(RCIMIWMessage e) {
    ZtIMessageModel chatModel = ZtIMessageModel(
        content: e.toJson()['text'],
        type: e.messageType,
        conversationId: e.targetId,
        sentStatus: e.sentStatus!,
        sentTime: e.sentTime!);
    RCIMIWUserInfo? userInfo = e.userInfo;

    if (e.messageType == RCIMIWMessageType.voice) {
      chatModel.content = e.toJson()['remote'];
      chatModel.duration = e.toJson()['duration'];
    }
    if (e.messageType == RCIMIWMessageType.file) {
      chatModel.content = e.toJson()['remote'];
      chatModel.filePath = e.toJson()['filePath'] ?? e.toJson()['local'];
      chatModel.filename = e.toJson()['name'];
    }
    if (e.messageType == RCIMIWMessageType.image) {
      chatModel.filePath = e.toJson()['filePath'] ?? e.toJson()['local'];
      chatModel.content = e.toJson()['remote'];
    }
    if (e.senderUserId == ZtAuthStore.to.userInfo.value.userId) {
      chatModel.userId = ZtAuthStore.to.userInfo.value.userId;
      chatModel.avatarUrl = ZtAuthStore.to.userInfo.value.avatarThumbUrl;
      chatModel.nickname = ZtAuthStore.to.userInfo.value.nickname;
    } else {
      if (userInfo?.userId == e.senderUserId) {
        chatModel.userId = userInfo!.userId;
        chatModel.avatarUrl = userInfo.portrait;
        chatModel.nickname = userInfo.name;
      } else if (e.userInfo?.extra != null) {
        final extraInfo = jsonDecode(e.userInfo!.extra!);
        chatModel.userId = extraInfo['userId'];
        chatModel.avatarUrl = extraInfo['avatar'];
        chatModel.nickname = extraInfo['nickname'];
      }
    }

    return chatModel;
  }
}
